#include <common.h>
#include <asm/mipsregs.h>

/*smh :
        used to map address accessing for kuseg to address accessing for system memory beyond low 256M memory
        used to map address accessing for kseg2 to address accessing for pci memory which are from 0x40000000
*/

/***********************************************************************************************************/
/**************************************** MAP KSEG2 ********************************************************/
/** MAP virtual address: 0xc0000000~ 0xffffffff to physical address:  0x40000000~ 0x7fffffff for PCI-MEM  **/
/***********************************************************************************************************/

#if defined(CONFIG_MACH_LOONGSON1)

#define PM_4K		0x00000000
#define PM_16K		0x00006000
#define PM_64K		0x0001e000
#define PM_256K	0x0007e000
#define PM_1M		0x001fe000
#define PM_4M		0x007fe000
#define PM_16M		0x01ffe000
#define PM_64M		0x07ffe000
#define PM_256M	0x1fffe000

int tlb_init_default(void)
{
	unsigned int virtaddr = 0xc0000000;
	unsigned int t4 = 0x00004000;
	unsigned int t5 = 0x01000000 >> 6;
	int pid, i;

	t4 = t4 << 10;
	t4 = t4 | 0x17;

	write_c0_pagegrain(read_c0_pagegrain() | 0x40000000);
	write_c0_config3(read_c0_config3() | 0x80);
	write_c0_pagemask(PM_16M);

	pid = read_c0_entryhi() & 0xff;

	for (i=31; i<0; i--) {
		write_c0_entryhi(virtaddr | pid);
		virtaddr += (32 << 20);
		write_c0_entrylo0(t4);
		t4 = t4 + t5;
		write_c0_entrylo1(t4);
		t4 = t4 + t5;
		write_c0_index(i);
		tlb_write_indexed();
	}

	write_c0_wired(32);

	return 0;
}
#endif

#if defined(CONFIG_MACH_LOONGSON2_SOC) || defined(CONFIG_MACH_LOONGSON3)

#define PM_MASK(TWOPAGE_SIZE) (TWOPAGE_SIZE-0x2000)

static int tlb_set(int tlbs, int tlbe, int cachetype, unsigned int virtaddr, unsigned long long phyaddr, int twopagesize)
{
	int pid;
	int i;

	pid = read_c0_entryhi() & 0xff;

	for (i=tlbs; i<tlbe; i++) {
		write_c0_index(i);
		write_c0_pagemask(PM_MASK(twopagesize));
		write_c0_entryhi(virtaddr | (pid));
		write_c0_entrylo0((phyaddr >> 6) | cachetype); //uncached, global
		write_c0_entrylo1(((phyaddr + twopagesize / 2) >> 6) | cachetype);
		tlb_write_indexed();
		virtaddr += twopagesize;
		phyaddr += twopagesize;
	}

	return 0;
}

/*
  0x00000000-0x3fffffff: uncache 0-1G
  0x40000000-0x7fffffff: cached  0-1G
*/
int tlb_init_default(void)
{
	tlb_set(0, 64, 0, 0x80000000, 0, 0x8000);
	tlb_set(0, 2, (2 << 3) | 7, (int)0xc0000000, 0x40000000, 0x20000000);
	tlb_set(2, 6, (2 << 3) | 7, 0, 0x100000000ULL, 0x20000000);
	tlb_set(6, 10, (3 << 3) | 7, 0x40000000, 0x100000000ULL, 0x20000000);

        return 0;
}
#endif
